package com.gitee.wsl.net.downloader.destination

import com.gitee.wsl.io.DestWriter
import com.gitee.wsl.io.Dir
import com.gitee.wsl.io.File
import com.gitee.wsl.net.downloader.part.Part
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.internal.SynchronizedObject
import kotlinx.coroutines.internal.synchronized
import kotlinx.io.IOException


@OptIn(InternalCoroutinesApi::class)
abstract class DownloadDestination(
    val outputFile: File,
) : SynchronizedObject(){

    //val outputFile = outputFile.canonicalFile.absoluteFile

    var outputSize: Long = -1

    protected val fileParts = mutableListOf<DestWriter>()
    protected var allPartsDownloaded = false
    protected var requestedToChangeLastModified:Long?=null

    protected open fun onAllFilePartsRemoved(){
        updateLastModified()
    }

    open fun onAllPartsCompleted() {
        allPartsDownloaded = true
        cleanUpJunkFiles()
    }

    open fun cleanUpJunkFiles() {}

    abstract fun getWriterFor(part: Part): DestWriter
    abstract fun canGetFileWriter(): Boolean

    protected open fun initializeOut(out: File) {
        out.createIfNoExists()
        /*val folder:Dir? = out.parent
        if (!folder!!.exists()) {
            if (folder.mkdirs()) {
                throw IOException()
            }
        }
        if (!out.exists()) {
            val newFile = out.createNewFile()
            if (!newFile) {
                throw IOException()
            }
        }*/
    }

    fun returnIfAlreadyHaveWriter(partId: Long): DestWriter? {
        synchronized(this){
            return fileParts.find {
                val condition = it.id == partId
//      if (condition) {
//        logger.info("part id$partId already have an associated file")
//      }
                condition
            }
        }
    }

    open fun deleteOutPutFile() {
        outputFile.delete()
    }

    abstract suspend fun prepareFile(onProgressUpdate: (Int?) -> Unit)
    abstract suspend fun isDownloadedPartsIsValid(): Boolean
    abstract fun flush()
    open fun onPartCancelled(part: Part){
        synchronized(this){
            val cleanAny=fileParts.removeAll {
                it.id==part.from
            }
            if (cleanAny){
                if (fileParts.isEmpty()){
                    onAllFilePartsRemoved()
                }
            }
        }
    }

    /**
     * specify last modified time to be used when this destination finish its work
     * for example file paused / finished
     */
    fun setLastModified(timestamp: Long?) {
        requestedToChangeLastModified=timestamp
    }

    protected fun updateLastModified(){
        runCatching<Unit> {
            requestedToChangeLastModified?.let {
                outputFile.setLastModified(it)
            }
        }
    }

    companion object{
        val OP = object:DownloadDestination(File("temp")){
            override fun getWriterFor(part: Part): DestWriter {
                TODO("Not yet implemented")
            }

            override fun canGetFileWriter(): Boolean {
                TODO("Not yet implemented")
            }

            override suspend fun prepareFile(onProgressUpdate: (Int?) -> Unit) {
                TODO("Not yet implemented")
            }

            override suspend fun isDownloadedPartsIsValid(): Boolean {
                TODO("Not yet implemented")
            }

            override fun flush() {
                TODO("Not yet implemented")
            }

        }
    }
}